﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Printing;
using System.Linq;
using System.Windows.Controls.Primitives;

namespace Gmantis.Controls.DataGrid
{
    internal class PrintManager
    {
        // Fields
        private Size _contentAreaSize = new Size();
        private readonly DataGrid _dataGrid;
        private PrinterFallbackSettings _fallBackSettings;
        private bool _isPrepared;
        private int _maxPages = 100;
        private bool _needsZoom;
        private Thickness _pageMargin = new Thickness(20.0);
        private int _physicalPageCount;
        private int _physicalPageIdx;
        private UIElement _prevPrintedLayout;
        private Size _printableArea = new Size();
        private readonly PrintDocument _printDoc;
        private Canvas _printingRoot;
        private double _scale = 1.0;
        private ScaleMode _scaleMode;
        private readonly List<HorzSubpageInfo> _subPages = new List<HorzSubpageInfo>();
        private bool _useDefaultPrinter;
        private readonly List<VertPageInfo> _vertPages = new List<VertPageInfo>();

        // Methods
        public PrintManager(DataGrid dataGrid)
        {
            this._dataGrid = dataGrid;
            this.ShowPageInfo = true;
            this._printDoc = new PrintDocument();
        }

        private void ApplyLayout(FrameworkElement element)
        {
            if (element != null)
            {
                element.Measure(new Size(element.Width, element.Height));
                element.Arrange(new Rect(0.0, 0.0, element.Width, element.Height));
                element.UpdateLayout();
            }
        }

        private static DataGrid CloneDataGrid(DataGrid dataGrid, IList<DataGridColumn> columns)
        {
            DataGrid targetObj = (DataGrid)CloneObject(dataGrid, false, new string[0]);
            targetObj.AutoGenerateColumns = false;
            targetObj.Height = GetTopDecorationsHeight(dataGrid) + 5.0;
            CopyProperties(targetObj, dataGrid, new string[] { "Name", "Resources", "SelectedIndex", "SelectedItem", "AutoGenerateColumns", "Height", "Margin", "CurrentColumn", "ItemsSource", "CurrentCell" });
            targetObj.Name = "DataGridPrintClone" + targetObj.GetHashCode().ToString();
            targetObj.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
            targetObj.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
            targetObj.ShowGroupingPanel = false;
            targetObj.CanUserAddRows = false;
            targetObj.CanUserFilter = false;
            targetObj.CanUserGroup = false;
            IList<DataGridColumn> list = columns ?? dataGrid.Columns;
            foreach (DataGridColumn column in list)
            {
                DataGridColumn item = (DataGridColumn)CloneObject(column, true, new string[] { "DisplayIndex", "SortState", "FilterState", "GroupState", "IsAutoGenerated" });
                item.Width = new DataGridLength(column.ActualWidth);
                targetObj.Columns.Add(item);
            }
            targetObj.ItemsSource = dataGrid.DataSourceView;
            if (dataGrid.GroupedColumns.Length > 0)
            {
                int num = Math.Min(dataGrid.Rows.Count, targetObj.Rows.Count);
                for (int i = 0; i < num; i++)
                {
                    DataGridGroupRow row = dataGrid.Rows[i] as DataGridGroupRow;
                    if ((row != null) && (row.GroupRowsVisibility != Visibility.Visible))
                    {
                        DataGridGroupRow row2 = targetObj.Rows[i] as DataGridGroupRow;
                        if (row2 != null)
                        {
                            row2.GroupRowsVisibility = row.GroupRowsVisibility;
                        }
                    }
                }
            }
            return targetObj;
        }

        private static object CloneObject(object sourceObj, bool copyProperties, params string[] excludeProperties)
        {
            if (sourceObj == null)
            {
                return null;
            }
            object targetObj = Activator.CreateInstance(sourceObj.GetType());
            Control control = targetObj as Control;
            if (control != null)
            {
                control.ApplyTemplate();
            }
            if (copyProperties)
            {
                CopyProperties(targetObj, sourceObj, excludeProperties);
            }
            return targetObj;
        }

        private static int CompareColumnOrder(DataGridColumn x, DataGridColumn y)
        {
            return (x.DisplayIndex - y.DisplayIndex);
        }

        private static void CopyProperties(object targetObj, object sourceObj, params string[] excludeProperties)
        {
            if ((targetObj != null) && (sourceObj != null))
            {
                bool flag = (excludeProperties == null) || (excludeProperties.Length == 0);
                IList<string> list = flag ? null : ((IList<string>)excludeProperties);
                foreach (PropertyInfo info in sourceObj.GetType().GetProperties())
                {
                    if (((info.GetSetMethod(false) != null) && (info.GetIndexParameters().Length == 0)) && (flag || !list.Contains(info.Name)))
                    {
                        try
                        {
                            info.SetValue(targetObj, info.GetValue(sourceObj, null), null);
                        }
                        catch
                        {
                        }
                    }
                }
            }
        }

        private static double GetLeftDecorationsWidth(DataGrid dataGrid)
        {
            DataGridHeadersVisibility headersVisibility = dataGrid.HeadersVisibility;
            Thickness borderThickness = dataGrid.BorderThickness;
            return (((((((headersVisibility == DataGridHeadersVisibility.All) || (headersVisibility == DataGridHeadersVisibility.Row)) ? dataGrid.RowHeaderWidth : 0.0) + (dataGrid.GroupedColumns.Length * Math.Max(0.0, dataGrid.IndentWidth))) + ((dataGrid.ShowVerticalFreezingSeparator == DataGridColumnFreezing.Left) ? dataGrid.VerticalFreezingSeparatorWidth : 0.0)) + borderThickness.Left) + borderThickness.Right);
        }

        private static DataGridColumn[] GetMappedColumns(IList<DataGridColumn> sourceColumns, IList<DataGridColumn> targetColumns)
        {
            if ((sourceColumns == null) || (targetColumns == null))
            {
                return null;
            }
            DataGridColumn[] columnArray = new DataGridColumn[sourceColumns.Count];
            for (int i = 0; i < sourceColumns.Count; i++)
            {
                string colName = sourceColumns[i].Name;
                columnArray[i] = targetColumns.First<DataGridColumn>(parColumn => parColumn.Name == colName);
            }
            return columnArray;
        }

        private UIElement GetNextPage(out bool hasMorePages)
        {
            UIElement page = this.GetPage(this._physicalPageIdx);
            this._physicalPageIdx++;
            hasMorePages = this._physicalPageIdx < this.PhysicalPageCount;
            return page;
        }

        internal UIElement GetPage(int physicalPageIdx)
        {
            if (!this._isPrepared)
            {
                this.PreparePrinting();
            }
            if (this._prevPrintedLayout != null)
            {
                this.PrintingRoot.Children.Remove(this._prevPrintedLayout);
                this._prevPrintedLayout = null;
            }
            int pageIdx = physicalPageIdx / this.SubPages.Count;
            int subpageIdx = physicalPageIdx % this.SubPages.Count;
            VertPageInfo info = this.VertPages[pageIdx];
            DataGrid printGrid = CloneDataGrid(this.SubPages[subpageIdx].PrintGrid, null);
            FrameworkElement element = this.GetPageLayoutElement(printGrid, pageIdx, subpageIdx, info.PageHeight);
            this.PrintingRoot.Children.Add(element);
            this.ApplyLayout(element);
            printGrid.View.ScrollManager.ScrollVertically(info.FirstRowIdx, 0.0, GeneratorDirection.Forward);
            printGrid.Height = info.PageHeight;
            this.ApplyLayout(element);
            this._prevPrintedLayout = element;
            return element;
        }

        public List<FrameworkElement> GetPageImages(out int logicalPageCount)
        {
            this.PreparePrinting();
            logicalPageCount = this.VertPages.Count;
            List<FrameworkElement> list = new List<FrameworkElement>(this.PhysicalPageCount);
            try
            {
                for (int i = 0; i < this.PhysicalPageCount; i++)
                {
                    FrameworkElement page = (FrameworkElement)this.GetPage(i);
                    page.IsHitTestVisible = false;
                    list.Add(page);
                }
            }
            finally
            {
                this.UnpreparePrinting();
            }
            return list;
        }

        private FrameworkElement GetPageLayoutElement(DataGrid printGrid, int pageIdx, int subpageIdx, double printGridHeight)
        {
            Border border = new Border
            {
                Padding = this.PageMargin
            };
            border.Width = this.PrintableArea.Width;
            border.Height = this.PrintableArea.Height;
            border.Background = new SolidColorBrush(Colors.Transparent);
            Grid grid = new Grid();
            border.Child = grid;
            RowDefinition definition = new RowDefinition
            {
                Height = new GridLength(this.PageHeaderHeight)
            };
            grid.RowDefinitions.Add(definition);
            RowDefinition definition2 = new RowDefinition
            {
                Height = new GridLength(this._contentAreaSize.Height)
            };
            grid.RowDefinitions.Add(definition2);
            int num = grid.RowDefinitions.Count - 1;
            RowDefinition definition3 = new RowDefinition
            {
                Height = new GridLength(this.PageFooterHeight)
            };
            grid.RowDefinitions.Add(definition3);
            int num2 = grid.RowDefinitions.Count - 1;
            if (printGrid != null)
            {
                if (this._needsZoom)
                {
                    Viewbox element = new Viewbox();
                    Grid.SetRow(element, num);
                    element.Width = this._contentAreaSize.Width;
                    element.Height = printGridHeight * this._scale;
                    element.Stretch = Stretch.Uniform;
                    element.HorizontalAlignment = HorizontalAlignment.Left;
                    element.VerticalAlignment = VerticalAlignment.Top;
                    element.Child = printGrid;
                    grid.Children.Add(element);
                }
                else
                {
                    Grid.SetRow(printGrid, num);
                    printGrid.HorizontalAlignment = HorizontalAlignment.Left;
                    printGrid.VerticalAlignment = VerticalAlignment.Top;
                    grid.Children.Add(printGrid);
                }
            }
            if (this.ShowPageInfo)
            {
                string str = (pageIdx + 1).ToString();
                if (this.SubPages.Count > 1)
                {
                    str = str + "-" + ((subpageIdx + 1)).ToString();
                }
                str = str + " / " + this.VertPages.Count.ToString();
                TextBlock block = new TextBlock
                {
                    VerticalAlignment = VerticalAlignment.Center,
                    HorizontalAlignment = HorizontalAlignment.Center,
                    TextAlignment = TextAlignment.Center,
                    Text = str
                };
                Grid.SetRow(block, num2);
                grid.Children.Add(block);
            }
            return border;
        }

        private static double GetTopDecorationsHeight(DataGrid dataGrid)
        {
            DataGridHeadersVisibility headersVisibility = dataGrid.HeadersVisibility;
            Thickness borderThickness = dataGrid.BorderThickness;
            return (((((headersVisibility == DataGridHeadersVisibility.All) || (headersVisibility == DataGridHeadersVisibility.Column)) ? dataGrid.ColumnHeaderHeight : 0.0) + borderThickness.Top) + borderThickness.Bottom);
        }

        private void PreparePrinting()
        {
            this.SubPages.Clear();
            this.VertPages.Clear();
            Grid grid = this.DataGrid._elementRoot;
            this._printingRoot = new Canvas();
            this._printingRoot.Clip = Geometry.Empty;
            grid.Children.Add(this._printingRoot);
            this._physicalPageIdx = 0;
            this._physicalPageCount = 0;
            this._contentAreaSize = new Size(Math.Max((double)0.0, (double)((this.PrintableArea.Width - this.PageMargin.Left) - this.PageMargin.Right)), Math.Max((double)0.0, (double)((((this.PrintableArea.Height - this.PageMargin.Top) - this.PageMargin.Bottom) - this.PageHeaderHeight) - this.PageFooterHeight)));
            bool flag = this.ScaleMode != ScaleMode.ActualSize;
            bool flag2 = this.ScaleMode == ScaleMode.SinglePage;
            List<DataGridColumn> list = new List<DataGridColumn>(this.DataGrid.Columns);
            list.Sort(new Comparison<DataGridColumn>(PrintManager.CompareColumnOrder));
            double leftDecorationsWidth = GetLeftDecorationsWidth(this.DataGrid);
            DataGrid grid2 = null;
            int num2 = Math.Max(0, Math.Min(this.DataGrid.FrozenColumnCount, list.Count));
            bool flag3 = num2 > 0;
            double num3 = 0.0;
            for (int i = 0; i < num2; i++)
            {
                DataGridColumn column = list[i];
                if (column.Visibility != Visibility.Collapsed)
                {
                    num3 += column.ActualWidth;
                }
            }
            double num5 = (this._contentAreaSize.Width - leftDecorationsWidth) - num3;
            if (num5 <= 15.0)
            {
                flag3 = false;
            }
            if (flag3)
            {
                leftDecorationsWidth += num3;
            }
            else
            {
                num2 = 0;
            }
            double num6 = leftDecorationsWidth;
            int firstColumnIdx = num2;
            int num8 = num2;
            while (num8 < list.Count)
            {
                DataGridColumn column2 = list[num8];
                if (grid2 == null)
                {
                    grid2 = CloneDataGrid(this.DataGrid, list);
                    this.PrintingRoot.Children.Add(grid2);
                    grid2.FrozenColumnCount = num2;
                }
                double num9 = (column2.Visibility != Visibility.Collapsed) ? column2.ActualWidth : 0.0;
                double num10 = num6 + num9;
                bool flag4 = !flag && (num10 > this._contentAreaSize.Width);
                if (flag4)
                {
                    if (num8 == firstColumnIdx)
                    {
                        num8++;
                        num6 = num10;
                    }
                }
                else
                {
                    num6 = num10;
                    num8++;
                    flag4 = num8 == list.Count;
                }
                if (flag4)
                {
                    int lastColumnIdx = num8 - 1;
                    this.SubPages.Add(new HorzSubpageInfo(grid2, firstColumnIdx, lastColumnIdx));
                    firstColumnIdx = lastColumnIdx + 1;
                    grid2.Width = num6;
                    grid2.UpdateLayout();
                    grid2 = null;
                    num6 = leftDecorationsWidth;
                }
            }
            if (this.SubPages.Count > 0)
            {
                for (int n = 0; n < this.SubPages.Count; n++)
                {
                    HorzSubpageInfo info = this.SubPages[n];
                    DataGridColumnCollection columns = info.PrintGrid.Columns;
                    for (int num13 = 0; num13 < columns.Count; num13++)
                    {
                        if ((num13 >= num2) && ((num13 < info.FirstColumnIdx) || (num13 > info.LastColumnIdx)))
                        {
                            columns[num13].Visibility = Visibility.Collapsed;
                        }
                    }
                    this.ApplyLayout(info.PrintGrid);
                }
            }
            DataGrid printGrid = this.SubPages[0].PrintGrid;
            double topDecorationsHeight = GetTopDecorationsHeight(printGrid);
            double height = this._contentAreaSize.Height;
            double num16 = height - topDecorationsHeight;
            printGrid.Height = height;
            this.ApplyLayout(printGrid);
            int count = printGrid.Rows.Count;
            int rowDisplayIndex = Math.Max(0, Math.Min(printGrid.FrozenTopRowsCount, count));
            double num19 = 0.0;
            for (int j = 0; j < rowDisplayIndex; j++)
            {
                num19 += printGrid.Rows[j].ActualHeight;
            }
            int num21 = Math.Max(0, Math.Min(printGrid.FrozenBottomRowsCount, count));
            int num22 = count - num21;
            double num23 = 0.0;
            for (int k = num22; k < count; k++)
            {
                num23 += printGrid.Rows[k].ActualHeight;
            }
            double num25 = num19 + num23;
            if ((num25 + 15.0) < num16)
            {
                topDecorationsHeight += num25;
            }
            else
            {
                rowDisplayIndex = 0;
                num21 = 0;
                num22 = count;
                foreach (HorzSubpageInfo info2 in this.SubPages)
                {
                    info2.PrintGrid.FrozenTopRowsCount = 0;
                    info2.PrintGrid.FrozenBottomRowsCount = 0;
                    this.ApplyLayout(info2.PrintGrid);
                }
            }
            double num26 = 1.0;
            if ((flag && !flag2) && (printGrid.Width > this._contentAreaSize.Width))
            {
                num26 = printGrid.Width / this._contentAreaSize.Width;
                height *= num26;
            }
            int num27 = 0;
            double num28 = topDecorationsHeight;
            if (rowDisplayIndex != 0)
            {
                printGrid.View.ScrollManager.ScrollVertically(rowDisplayIndex, 0.0, GeneratorDirection.Forward);
                this.ApplyLayout(printGrid);
            }
            for (int m = rowDisplayIndex; (m < num22) && (this.VertPages.Count < this.MaxPages); m++)
            {
                double actualHeight = printGrid.Rows[m].ActualHeight;
                double num31 = num28 + actualHeight;
                if (!flag2 && (num31 > height))
                {
                    VertPageInfo item = new VertPageInfo
                    {
                        FirstRowIdx = num27
                    };
                    if (m == num27)
                    {
                        item.LastRowIdx = m;
                        item.PageHeight = num31;
                        num28 = topDecorationsHeight;
                    }
                    else
                    {
                        item.LastRowIdx = m - 1;
                        item.PageHeight = num28;
                        num28 = topDecorationsHeight + actualHeight;
                    }
                    num27 = item.LastRowIdx + 1;
                    this.VertPages.Add(item);
                }
                else
                {
                    num28 = num31;
                }
                if ((m == printGrid.Viewport.LastVisibleRow) && ((m + 1) < count))
                {
                    printGrid.View.ScrollManager.ScrollVertically(m + 1, 0.0, GeneratorDirection.Forward);
                    this.ApplyLayout(printGrid);
                }
            }
            if ((num27 < count) && (this.VertPages.Count < this.MaxPages))
            {
                VertPageInfo info5 = new VertPageInfo
                {
                    FirstRowIdx = num27,
                    LastRowIdx = count - 1,
                    PageHeight = num28
                };
                this.VertPages.Add(info5);
            }
            printGrid.View.ScrollManager.ScrollVertically(0, 0.0, GeneratorDirection.Forward);
            this.ApplyLayout(printGrid);
            this._physicalPageCount = this.VertPages.Count * this.SubPages.Count;
            this._needsZoom = (num26 > 1.0) || (flag2 && (num28 > this._contentAreaSize.Height));
            if (this._needsZoom)
            {
                this._scale = 1.0 / Math.Max(num26, num28 / this._contentAreaSize.Height);
            }
            else
            {
                this._scale = 1.0;
            }
            this._isPrepared = true;
        }

        public void Print(string documentName)
        {
            this.PrintDoc.PrintPage += new EventHandler<PrintPageEventArgs>(this.PrintDoc_PrintPage);
            this.PrintDoc.EndPrint += new EventHandler<EndPrintEventArgs>(this.PrintDoc_EndPrint);
            if (this.FallBackSettings != null)
            {
                this.PrintDoc.Print(documentName, this.FallBackSettings, this.UseDefaultPrinter);
            }
            else
            {
                this.PrintDoc.Print(documentName);
            }
        }

        private void PrintDoc_EndPrint(object sender, EndPrintEventArgs e)
        {
            this.UnpreparePrinting();
        }

        private void PrintDoc_PrintPage(object sender, PrintPageEventArgs e)
        {
            bool flag;
            this._printableArea = e.PrintableArea;
            e.PageVisual = this.GetNextPage(out flag);
            e.HasMorePages = flag;
        }

        private void UnpreparePrinting()
        {
            if (this._prevPrintedLayout != null)
            {
                this.PrintingRoot.Children.Remove(this._prevPrintedLayout);
                this._prevPrintedLayout = null;
            }
            foreach (HorzSubpageInfo info in this.SubPages)
            {
                this.PrintingRoot.Children.Remove(info.PrintGrid);
            }
            this.DataGrid._elementRoot.Children.Remove(this.PrintingRoot);
            this.SubPages.Clear();
            this.VertPages.Clear();
            this._isPrepared = false;
        }

        // Properties
        private DataGrid DataGrid
        {
            get
            {
                return this._dataGrid;
            }
        }

        public PrinterFallbackSettings FallBackSettings
        {
            get
            {
                return this._fallBackSettings;
            }
            set
            {
                this._fallBackSettings = value;
            }
        }

        internal bool IsPrepared
        {
            get
            {
                return this._isPrepared;
            }
        }

        public int MaxPages
        {
            get
            {
                return this._maxPages;
            }
            set
            {
                this._maxPages = value;
            }
        }

        private double PageFooterHeight
        {
            get
            {
                return 40.0;
            }
        }

        private double PageHeaderHeight
        {
            get
            {
                return 0.0;
            }
        }

        public Thickness PageMargin
        {
            get
            {
                return this._pageMargin;
            }
            set
            {
                this._pageMargin = value;
            }
        }

        internal int PhysicalPageCount
        {
            get
            {
                return this._physicalPageCount;
            }
        }

        internal Size PrintableArea
        {
            get
            {
                return this._printableArea;
            }
            set
            {
                this._printableArea = value;
            }
        }

        private PrintDocument PrintDoc
        {
            get
            {
                return this._printDoc;
            }
        }

        private Canvas PrintingRoot
        {
            get
            {
                return this._printingRoot;
            }
        }

        public ScaleMode ScaleMode
        {
            get
            {
                return this._scaleMode;
            }
            set
            {
                this._scaleMode = value;
            }
        }

        public bool ShowPageInfo { get; set; }

        private List<HorzSubpageInfo> SubPages
        {
            get
            {
                return this._subPages;
            }
        }

        public bool UseDefaultPrinter
        {
            get
            {
                return this._useDefaultPrinter;
            }
            set
            {
                this._useDefaultPrinter = value;
            }
        }

        private List<VertPageInfo> VertPages
        {
            get
            {
                return this._vertPages;
            }
        }
    }
}
